﻿#region using

using System;
using System.Text;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using CipherLib;

#endregion

namespace CipherLib.Cipher
{
    public class CipherEnvelope : CipherBaseLib, ICipher
    {
        /// <summary>
        /// The storelocation for the
        /// encryption. The decryption uses the information from the encrypted envelope (text).
        /// </summary>
        public CipherEnvelope(string subjectName) : base(subjectName) { }

        /// <summary>
        /// The storename for the encryption. 
        /// The decryption uses the information from the encrypted envelope (text).
        /// </summary>
        public CipherEnvelope(string subjectName, StoreName defaultEncryptStoreName, 
            StoreLocation defaultStoreLocation) :
            base(subjectName, defaultEncryptStoreName, defaultStoreLocation) { }


        #region en/decryption

        /// <summary>
        /// Encrypts a string to a X.509 envelope.
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public virtual string EncryptString(string message)
        {
            byte[] msgBytes = Encoding.UTF8.GetBytes(message);
            string base64DecryptedContent = Convert.ToBase64String(msgBytes);

            X509Certificate2 certificate = base.GetCertificate(EncryptStoreName, StoreLocation);

            byte[] encodedEnvelopedCms = EncryptMsg(msgBytes, certificate);

            return Convert.ToBase64String(encodedEnvelopedCms);
        }

        /// <summary>
        /// Decrypts a X.509 envelope to a string
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public virtual string DecryptString(string message)
        {
            // Get original bytes
            byte[] decryptedBytes = DecryptMsg(Convert.FromBase64String(message));
            string decryptedText = Encoding.ASCII.GetString(decryptedBytes);

            return decryptedText;
        }

        #endregion

        #region private / protected methods

        protected static byte[] EncryptMsg(Byte[] message, X509Certificate2 recipientCert)
        {
            Oid contentOid = new Oid(AppCommon.CONT_OID, AppCommon.CONT_FRIENDLY_NAME);
            Oid algorithmOid = new Oid(AppCommon.ALGO_OID, AppCommon.ALGO_FRIENDLY_NAME);
            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(algorithmOid);
            ContentInfo content = new ContentInfo(contentOid, message);

            EnvelopedCms envelopedCms = new EnvelopedCms(SubjectIdentifierType.NoSignature, 
                content, algorithmIdentifier);

            CmsRecipient recipient = new CmsRecipient(recipientCert);

            if (envelopedCms == null)
            {
                throw new ApplicationException(AppCommon.EX_ENVELOPE_CMS_RECIPIENT_ERROR);
            }

            envelopedCms.Encrypt(recipient);

            return envelopedCms.Encode();
        }

        private static Byte[] DecryptMsg(byte[] encodedEnvelopedCms)
        {
            if (encodedEnvelopedCms == null)
            {
                throw new ApplicationException(AppCommon.EX_ENVELOPE_CMS_EMPTY);
            }
            EnvelopedCms envelopedCms = new EnvelopedCms();

            envelopedCms.Decode(encodedEnvelopedCms);

            if (envelopedCms == null)
            {
                throw new ApplicationException(AppCommon.EX_ENVELOPE_CMS_INIT);
            }
            if (envelopedCms.RecipientInfos == null)
            {
                throw new ApplicationException(AppCommon.EX_ENVELOPE_CMS_RECIPIENTINFO_NULL);
            }
            if (envelopedCms.RecipientInfos.Count == 0)
            {
                throw new ApplicationException(AppCommon.EX_ENVELOPE_CMS_RECIPIENTINFO_ERROR);
            }

            envelopedCms.Decrypt(envelopedCms.RecipientInfos[0]);

            if (envelopedCms.ContentInfo == null)
            {
                throw new ApplicationException(AppCommon.EX_ENVELOPE_CMS_ERROR);
            }

            return envelopedCms.ContentInfo.Content;
        }

        #endregion

    }
}